package com.vdimitr.happycube.puzzle.piece.construction;

import java.util.ArrayList;
import java.util.List;

import com.vdimitr.happycube.puzzle.piece.PuzzlePiece;
import com.vdimitr.happycube.puzzle.piece.PuzzlePieceColor;
import com.vdimitr.happycube.puzzle.piece.PuzzlePieceConstants;
import com.vdimitr.happycube.puzzle.piece.PuzzlePieceFlip;
import com.vdimitr.happycube.puzzle.piece.PuzzlePieceRotation;

/**
 * This class is responsible for creating valid puzzle pieces based on color,
 * number, flip and rotation and setting the correct matrix representation based
 * on the puzzle piece's state.
 * 
 * @author vasileios.dimitriadis
 * 
 */
public class PuzzlePieceFactory {

	/**
	 * Knows how to load a puzzle piece with its correct matrix representation
	 */
	private static PuzzlePieceMatrixLoader puzzlePieceMatrixLoader = new PuzzlePieceMatrixLoader();

	/**
	 * We only need one factory!
	 */
	private static PuzzlePieceFactory singleton = new PuzzlePieceFactory();

	/**
	 * No need to instantiate
	 */
	private PuzzlePieceFactory() {
	}

	/**
	 * Not synchronized!!!
	 * 
	 * @return a single instance of the class
	 */
	public static PuzzlePieceFactory getInstance() {
		return singleton;
	}

	/**
	 * Creates all the puzzle pieces of a specific color with default state and
	 * filled with their respective matrix representation.
	 * 
	 * @param color
	 *            the color family
	 * @return all the puzzle pieces of the specified color family at default
	 *         state
	 */
	public List<PuzzlePiece> createPuzzlePieces(PuzzlePieceColor color) {
		List<PuzzlePiece> puzzlePieces = new ArrayList<PuzzlePiece>();
		for (int puzzlePieceNumber = 1; puzzlePieceNumber <= PuzzlePieceConstants.NUMBER_OF_PUZZLEPIECES; puzzlePieceNumber++) {
			PuzzlePiece puzzlePiece = createPuzzlePiece(color,
					puzzlePieceNumber);
			puzzlePieces.add(puzzlePiece);
		}
		return puzzlePieces;
	}

	/**
	 * Creates all the puzzle pieces of a specified puzzle piece, combining flip
	 * and rotation and adjusting the matrix representation to match that state.
	 * 
	 * @param puzzlePiece
	 *            the original puzzle piece
	 * @return all the variations of the specified puzzle piece
	 */
	public List<PuzzlePiece> createPuzzlePieceVariations(PuzzlePiece puzzlePiece) {
		List<PuzzlePiece> puzzlePieceVariations = new ArrayList<PuzzlePiece>();
		for (PuzzlePieceFlip flip : PuzzlePieceFlip.values()) {
			for (PuzzlePieceRotation rotation : PuzzlePieceRotation.values()) {
				PuzzlePiece puzzlePieceVariation = createPuzzlePiece(
						puzzlePiece.getColor(),
						puzzlePiece.getPuzzlePieceNumber(), flip, rotation);
				puzzlePieceVariations.add(puzzlePieceVariation);
			}
		}
		return puzzlePieceVariations;
	}

	/**
	 * Creates a puzzle piece of the specified color and number not flipped or
	 * rotated.
	 * 
	 * @param color
	 *            the color family
	 * @param puzzlePieceNumber
	 *            the number of the puzzle piece
	 * @return a puzzle piece with default state (no flip, no rotation) with its
	 *         matrix representation set
	 */
	public PuzzlePiece createPuzzlePiece(PuzzlePieceColor color,
			int puzzlePieceNumber) {
		PuzzlePiece puzzlePiece = new PuzzlePiece(color, puzzlePieceNumber);
		puzzlePieceMatrixLoader.loadPuzzlePieceMatrix(puzzlePiece);
		return puzzlePiece;
	}

	/**
	 * Creates a puzzle piece of specific color, number and state (flip,
	 * rotation)
	 * 
	 * @param color
	 *            the color family
	 * @param puzzlePieceNumber
	 *            the number of the puzzle piece
	 * @param flip
	 *            whether it is flipped or not
	 * @param rotation
	 *            rotation count
	 * @return a puzzle piece with defined state (flip, rotation) and its matrix
	 *         representation set to match that state
	 */
	public PuzzlePiece createPuzzlePiece(PuzzlePieceColor color,
			int puzzlePieceNumber, PuzzlePieceFlip flip,
			PuzzlePieceRotation rotation) {
		PuzzlePiece puzzlePiece = new PuzzlePiece(color, puzzlePieceNumber,
				flip, rotation);
		puzzlePieceMatrixLoader.loadPuzzlePieceMatrix(puzzlePiece);
		return puzzlePiece;
	}

}
